﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using FFmpegVideoClip.Processing;
using UtilityToolsCollect.ObjectsLibrary.ProcessManage;


namespace FFmpegVideoClip.Lib
{
    public class ThumbCancelSource
    {
        private static SpinLock m_SpimLock = new();
        private static List<ThumbCancelSource> CancelSourceList = new();
        public static ThumbCancelSource Create() { ThumbCancelSource result = new(); Add(result); return result; }
        private static void Add(ThumbCancelSource CancelSource) { bool m_LockTaken = false; m_SpimLock.Enter(ref m_LockTaken); CancelSourceList.Add(CancelSource); m_SpimLock.Exit(); }
        private static void Remove(ThumbCancelSource CancelSource) { bool m_LockTaken = false; m_SpimLock.Enter(ref m_LockTaken); CancelSourceList.Remove(CancelSource); m_SpimLock.Exit(); }
        public static void CancelAll() { CancelSourceList.ToArray().ForEach(p => { if (!p.IsClear) { if (p.IsCancellationRequested) p.Cancel(); p.Clear(); } }); }
        //******************************************************//
        private ThumbCancelSource() { CancelSource = new(); }
        private object LockToken = new();
        public bool IsCancellationRequested { get; private set; } = false;
        public bool IsClear { get; private set; }
        public event Action? OnCancel;
        public CancellationTokenSource CancelSource;
        public CancellationToken Token => IsClear == false ? CancelSource.Token : throw new ObjectDisposedException($"当前[{GetType().FullName}]，中的类型为[{typeof(CancellationToken).FullName}]的属性[{nameof(Token)}]获取失败，此对象以调用[{nameof(Clear)}]方法进行清理，并等待CLR回收，该对象已丢弃，无法继续使用");
        public void Cancel() { lock (LockToken) { if (IsClear) return; CancelSource.Cancel(); OnCancel?.Invoke(); IsCancellationRequested = true; } }
        protected virtual void OnClear() { Remove(this); CancelSource.Dispose(); CancelSource = null!; OnCancel = null; }
        public void Clear() { lock (LockToken) { if (!IsClear) { OnClear(); IsClear = true; } } }
    }
    
    [Obsolete] internal class FFmpegGetThumbs(int thumbWidth)
    {
        public record class ThumbsResultData(TimeSpan PTSTime, BitmapSource ThumbImageSource);
        private class ThumbsProcessInfo
        {
            private readonly static Regex SizeMatch = new Regex(@"(?<=rawvideo.+rgb24.+progressive.*?)\d+x\d+", RegexOptions.Compiled | RegexOptions.IgnoreCase);
            private readonly static Regex FrameTime = new Regex(@"(?<=pts_time:)\d+(\.\d+)?", RegexOptions.Compiled | RegexOptions.IgnoreCase);

            private StringBuilder m_RunInfo = new StringBuilder();
            private List<TimeSpan> m_FramesTime = new();
            public ReadOnlySpan<TimeSpan> FramesTime => m_FramesTime.ToArray();
            public bool IsError { get; private set; } = false;
            public string RunInfo => m_RunInfo.ToString();
            public bool IsRunInfoEmpty => m_RunInfo.Length < 1;
            public bool IsSizeMatchComplete { get; private set; } = false;
            public event Action<int, int>? OnGetSize;
            public void SetTaskInfo(string? value)
            {
                if (string.IsNullOrWhiteSpace(value))
                    return;
                Match match = FrameTime.Match(value);
                if (match.Success)
                {
                    try { m_FramesTime.Add(TimeSpan.FromSeconds(double.Parse(match.Value))); }
                    catch (Exception exc)
                    {
                        IsError = true;
                        LogManage.WriteExceptionInfo($"FFmpeg缩略图转换帧时间结果异常:捕捉信息:{match.Value}", exc, true);
                    }
                }
                else if (!IsSizeMatchComplete)
                {
                    match = SizeMatch.Match(value);
                    if (match.Success)
                    {

                        try
                        {
                            int[] size = match.Value.Split('x').Select(int.Parse).ToArray();
                            OnGetSize?.Invoke(size[0], size[1]);
                        }
                        catch (Exception exc)
                        {
                            LogManage.WriteExceptionInfo($"FFmpeg缩略图转换高宽结果异常:捕捉信息:{match.Value}", exc, true);
                            IsError = true;
                        }
                        IsSizeMatchComplete = true;
                    }
                }
                m_RunInfo.AppendLine(value);
                LogManage.WriteLineNewInfo(value,true);
            }
            public void Free() { m_RunInfo.Clear(); m_RunInfo = null!; m_FramesTime.Clear(); m_FramesTime = null!; OnGetSize = null!; }
        }
        private int ThumbWidth = thumbWidth;
        private ThumbsResultData[] InternalReadThumbs(string FilePath, TimeSpan Start, TimeSpan End, double FPS, ThumbCancelSource CancellSource)
        {
            int n;
            BitmapSource bitmapSource;
            Process? ffmpeg = new();
            ProcessShell ffmepgShell = ProcessShell.Create(ffmpeg);
            ThumbsProcessInfo ProcessInfo = new();
            Rgb24VideoFrameData VideoFrameData = null!;
            List<BitmapSource> BitmapSourceList = new();
            List<ThumbsResultData> Result = new();
            ProcessInfo.OnGetSize += (width, height) => { VideoFrameData = new(width, height); };
            try
            {
                CancellSource.OnCancel += () => ffmepgShell.Kill(false);
                ffmpeg.StartInfo.FileName = LosslessSplitTask.FFProcessor;
                ffmpeg.StartInfo.Arguments = Start == TimeSpan.Zero ?
                $"-hide_banner -an -i \"{FilePath}\" -t {End.ToString(Tools.TimeSpan_hhmmssfff)} -vf fps={FPS},showinfo,scale={ThumbWidth}:-1 -pix_fmt rgb24 -c:v rawvideo -f image2pipe -"
                : $"-hide_banner -an -ss {Start.ToString(Tools.TimeSpan_hhmmssfff)} -i \"{FilePath}\" -t {(End - Start).ToString(Tools.TimeSpan_hhmmssfff)} -vf fps={FPS},showinfo,scale={ThumbWidth}:-1 -pix_fmt rgb24 -c:v rawvideo -f image2pipe -";
                ffmpeg.StartInfo.UseShellExecute = false;
                ffmpeg.StartInfo.CreateNoWindow = true;
                ffmpeg.StartInfo.RedirectStandardOutput = true;
                ffmpeg.StartInfo.RedirectStandardError = true;

                ffmpeg.ErrorDataReceived += (s, e) => { if (!string.IsNullOrWhiteSpace(e.Data)) { ProcessInfo.SetTaskInfo(e.Data); } };

                ffmpeg.Start();
                ffmpeg.BeginErrorReadLine();

                while (VideoFrameData == null) { Thread.Sleep(100);if (CancellSource.IsCancellationRequested) return Result.ToArray(); }
                Span<byte> cache = new byte[VideoFrameData.FrameSizeLength];
                while ((n = ffmpeg.StandardOutput.BaseStream.Read(cache)) > 1)
                {
                    if (CancellSource.IsCancellationRequested) return Result.ToArray();
                    VideoFrameData.ReadFrame(cache.Slice(0, n).ToArray());
                }

                ffmpeg?.WaitForExit();
                if (CancellSource.IsCancellationRequested)
                    return Result.ToArray();
                double dip = 0;
                Application.Current.Dispatcher.Invoke(() => { dip = VisualTreeHelper.GetDpi(Application.Current.MainWindow).PixelsPerDip;
                foreach (Rgb24VideoFrameData.FrameByteData item in VideoFrameData.FrameDatas)
                {
                    bitmapSource = BitmapSource.Create(VideoFrameData.Width, VideoFrameData.Height, dip, dip, PixelFormats.Rgb24, null, item.FrameImageData.ToArray(), VideoFrameData.Stride);
                    BitmapSourceList.Add(bitmapSource);
                }
                });
                for (int i = 0; i < BitmapSourceList.Count; i++)
                {
                    Result.Add(new(ProcessInfo.FramesTime.Length > i ? ProcessInfo.FramesTime[i] : TimeSpan.Zero, BitmapSourceList[i]));
                }

                return Result.ToArray();
            }
            catch (Exception exc) { LogManage.WriteExceptionInfo($"FFmpeg执行异常:参数[{ffmpeg.StartInfo.Arguments}]", exc, true); return Result.ToArray(); }
            finally { ffmepgShell.Dispose(); CancellSource.Clear(); VideoFrameData?.Free(); ProcessInfo.Free(); }
        }


        /*  调用说明
        int ThumbWidth = (int)(ActualHeight * mec.VideoAspectRatioValue);
        FFmpegGetThumbs getThumbs = new(ThumbWidth);
        ConfigCancelSource();
        int FrameCount = (int)ActualWidth / ThumbWidth + 1;
        //var fps = FrameCount / (Maximum - Minimum);

        

        //ThumbsData = await getThumbs.ReadThumbs(FilePath, CurrentZoomMinTime, CurrentZoomMaxTime, ThumbWidth / ActualWidth, CancelSource!);
        //ThumbsData = await getThumbs.ReadThumbs(FilePath, CurrentZoomMinTime, CurrentZoomMaxTime, fps, CancelSource!);

        //  渲染
            if (ThumbsData.Length < 1)
                return;

            double Width = ThumbsData[0].ThumbImageSource.PixelWidth, CurrentPos = 0;

            foreach (ThumbsResultData item in ThumbsData)
            {
                dc.DrawImage(item.ThumbImageSource, new Rect(new System.Windows.Point(CurrentPos, 0), new System.Windows.Point(CurrentPos + Width, ActualHeight)));
                CurrentPos += Width;
            }
        */
        [Obsolete]public async Task<ThumbsResultData[]> ReadThumbs(string FilePath, TimeSpan Start, TimeSpan End, double FPS, ThumbCancelSource CancellSource)
            => await Task.Run(() => InternalReadThumbs(FilePath, Start, End, FPS, CancellSource), CancellSource.CancelSource.Token);
    }

    [Obsolete] internal class Rgb24VideoFrameData(int width, int height)
    {
        public class FrameByteData(int Size)
        {
            public readonly int FrameSize = Size;
            private byte[] Data = new byte[Size];
            public ReadOnlySpan<byte> FrameImageData => Data;
            public int Length { get; private set; }
            public int Remaining => FrameSize - Length;
            public bool Write(byte[] bytes)
            {
                if (Remaining < 1)
                    return false;
                if (Remaining < bytes.Length)
                    return false;
                Array.Copy(bytes, 0, Data, Length, bytes.Length);
                Length += bytes.Length;
                return true;
            }
            public void Free() => Data = null!;
        }
        private const int NB_COMPONENTS = 3;
        public int Width { get; private set; } = width;
        public int Height { get; private set; } = height;
        public readonly int FrameSizeLength = width * height * NB_COMPONENTS;
        public readonly int Stride = width * NB_COMPONENTS;
        private List<FrameByteData> m_FrameDatas = new();
        public ReadOnlySpan<FrameByteData> FrameDatas => m_FrameDatas.ToArray();
        private FrameByteData GetFrameData()
        {
            if (m_FrameDatas.Count < 1)
            {
                m_FrameDatas.Add(new(FrameSizeLength));
                return m_FrameDatas[0];
            }
            FrameByteData result = m_FrameDatas.Last();
            if (result.Remaining > 0)
                return result;
            result = new(FrameSizeLength);
            m_FrameDatas.Add(result);
            return result;
        }
        public void ReadFrame(byte[] bytes)
        {
            FrameByteData fd = GetFrameData();
            if (fd.Write(bytes))
                return;
            fd = new(FrameSizeLength);
            if (!fd.Write(bytes))
                throw new IndexOutOfRangeException("大小超出，轻检查缓存大小和数据是否匹配");
            m_FrameDatas.Add(fd);
        }
        public void ReadAllFrame(Span<Byte> bytes)
        {
            for (int i = 0; i < bytes.Length; i += FrameSizeLength)
                ReadFrame(bytes.Slice(i, i + FrameSizeLength).ToArray());
        }

        public void Free() { m_FrameDatas.ForEach(p => p.Free()); m_FrameDatas.Clear(); m_FrameDatas = null!; }
    }

}
